#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
新版价格策略分析平台
重构版本 - 保持原有代码不变，提供新的实现方案
"""

import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta, date
import os
import sys
from pathlib import Path
import logging
from typing import Dict, List, Optional, Tuple, Any
import asyncio
import concurrent.futures
import io
import re
from sqlalchemy import text
import openpyxl
from dataclasses import dataclass
import threading
import schedule
import time
from enum import Enum

# 添加项目根目录到Python路径
project_root = Path(__file__).parent
sys.path.append(str(project_root))

# 导入新的模块
from src.core.database import DatabaseManager
from src.core.price_engine import PriceStrategyEngine
from src.core.data_processor import DataProcessor
from src.core.strategy_selector import PriceStrategySelector, ProductMetrics, PriceStrategy, StrategyRecommendation
from src.ui.components import UIComponents
from src.config.settings import AppConfig
from src.utils.logger import setup_logger
import matplotlib.font_manager as fm


# 设置日志
logger = setup_logger(__name__)


# 图片中的中文展示
font_path = 'font/SimHei.ttf'
fm.fontManager.addfont(path=font_path)
prop = fm.FontProperties(fname=font_path)
plt.rcParams['font.sans-serif'] = prop.get_name()  # 设置中文字体为黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题


# 从show_tables.py导入的函数
def tidy_num(x):
    """Cast numeric-like values to int if whole, else rounded float. Keep None for NA."""
    if pd.isna(x):
        return None
    try:
        xf = float(x)
        xi = int(xf)
        return xi if abs(xf - xi) < 1e-9 else round(xf, 6)
    except Exception:
        return x

def compute_unique_sets(df: pd.DataFrame):
    # normalize columns
    df = df.copy()
    df.columns = [str(c).strip() for c in df.columns]
    required = {"package_code", "min_price_limit", "price"}
    missing = required - set(df.columns)
    if missing:
        raise ValueError(f"Missing required columns: {missing}")

    # keep only needed cols
    pairs = df[["package_code", "min_price_limit", "price"]].copy()
    pairs["min_price_limit"] = pairs["min_price_limit"].map(tidy_num)
    pairs["price"] = pairs["price"].map(tidy_num)
    pairs = pairs.dropna(subset=["min_price_limit", "price"])

    # signature per package: sorted unique pairs
    sig_by_pkg = (
        pairs.groupby("package_code", dropna=False)
             .apply(lambda g: tuple(sorted({(g["min_price_limit"].iloc[i], g["price"].iloc[i]) for i in range(len(g))})))
             .to_dict()
    )

    # invert: signature -> [packages]
    packages_by_signature = {}
    for pkg, sig in sig_by_pkg.items():
        packages_by_signature.setdefault(sig, []).append(pkg)

    return sig_by_pkg, packages_by_signature

def build_summary_df(packages_by_signature: dict) -> pd.DataFrame:
    rows = []
    for i, (sig, pkgs) in enumerate(packages_by_signature.items(), start=1):
        pairs_str = "; ".join([f"{a}->{b}" for a, b in sig])
        rows.append({
            "set_id": i,
            "pair_count": len(sig),
            "pairs": pairs_str,
            "package_codes": ", ".join(map(str, sorted(set(pkgs))))
        })
    return pd.DataFrame(rows).sort_values("set_id").reset_index(drop=True)

def normalize_series_to_fraction(s: pd.Series):
    """把序列统一到 [0,1] 小数"""
    s = pd.to_numeric(s, errors="coerce")
    if s.dropna().empty:
        return s
    max_abs = s.dropna().abs().max()
    return s / 100.0 if max_abs > 1.5 else s

def _iphone_generation(name: str):
    """提取 iPhone 代数"""
    m = re.search(r"iPhone\s*(\d+)", str(name), re.I)
    return int(m.group(1)) if m else 10**9

def _is_pro_max(name: str):
    return re.search(r"Pro\s*Max", str(name), re.I) is not None

def _is_pro_only(name: str):
    return (re.search(r"\bPro\b", str(name), re.I) is not None) and (not _is_pro_max(name))

def preferred_iphone_order(product_names):
    """自动生成顺序：按代数升序分组；同代内 标准款 -> Pro -> Pro Max"""
    names = list(dict.fromkeys(product_names))
    buckets: dict[int, list[str]] = {}
    for n in names:
        buckets.setdefault(_iphone_generation(n), []).append(n)

    ordered = []
    for gen in sorted(buckets.keys()):
        group = buckets[gen]
        base = [x for x in group if (not _is_pro_only(x)) and (not _is_pro_max(x))]
        pro  = [x for x in group if _is_pro_only(x)]
        pmax = [x for x in group if _is_pro_max(x)]
        ordered.extend(base + pro + pmax)
    return ordered

# 页面配置
st.set_page_config(
    page_title="智能打\"XX\"侠",
    page_icon="📊",
    layout="wide",
    initial_sidebar_state="expanded"
)

# 自定义CSS样式
st.markdown("""
<style>
    .main-header {
        font-size: 2.5rem;
        color: #FF5733;
        text-align: center;
        margin-bottom: 1rem;
        background: linear-gradient(90deg, #FF5733, #4B8BBE);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        background-clip: text;
    }
    .metric-card {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        padding: 1rem;
        border-radius: 10px;
        color: white;
        text-align: center;
        box-shadow: 0 4px 15px rgba(0,0,0,0.1);
    }
    .metric-value {
        font-size: 2rem;
        font-weight: bold;
        margin-bottom: 0.5rem;
    }
    .metric-label {
        font-size: 0.9rem;
        opacity: 0.9;
    }
    .success-box {
        background-color: #d4edda;
        border: 1px solid #c3e6cb;
        border-radius: 5px;
        padding: 1rem;
        margin: 1rem 0;
    }
    .error-box {
        background-color: #f8d7da;
        border: 1px solid #f5c6cb;
        border-radius: 5px;
        padding: 1rem;
        margin: 1rem 0;
    }
</style>
""", unsafe_allow_html=True)

@dataclass
class ProcessingResult:
    """处理结果数据类"""
    success: bool
    data: Optional[pd.DataFrame] = None  # chosen_df
    sku_level_df: Optional[pd.DataFrame] = None  # sku_level_df
    selected_chosen_results: Optional[pd.DataFrame] = None  # selected_chosen_results
    message: str = ""
    processing_time: float = 0.0
    error_details: Optional[str] = None

def _format_ahs_coupon_data(summary_df):
    """格式化爱回收优惠券数据，将pairs字段拆分"""
    try:
        formatted_rows = []
        for _, row in summary_df.iterrows():
            pairs_str = row.get('pairs', '')
            package_codes = row.get('package_codes', '')
            
            if pairs_str and pairs_str != 'nan':
                # 解析pairs字符串，格式如 "200->30; 400->50; 800->120; 1200->180; 2000->265; 3000->280; 4000->290; 4800->300"
                pairs = pairs_str.split(';')
                for i, pair in enumerate(pairs):
                    pair = pair.strip()
                    if '->' in pair:
                        price_limit, coupon_amount = pair.split('->')
                        price_limit = int(price_limit.strip())
                        coupon_amount = int(coupon_amount.strip())
                        
                        # 计算价格上限
                        if i < len(pairs) - 1:
                            # 不是最后一个，取下一个价格下限-1
                            next_pair = pairs[i + 1].strip()
                            if '->' in next_pair:
                                next_price_limit = int(next_pair.split('->')[0].strip())
                                price_upper = next_price_limit - 1
                            else:
                                price_upper = 999999999
                        else:
                            # 最后一个，设置为999999999
                            price_upper = 999999999
                        
                        formatted_rows.append({
                            '价格下限': price_limit,
                            '价格上限': price_upper,
                            '优惠券额度': coupon_amount,
                            'package_codes': package_codes
                        })
        
        if formatted_rows:
            return pd.DataFrame(formatted_rows)
        else:
            return summary_df
    except Exception as e:
        logger.error(f"格式化爱回收优惠券数据失败: {str(e)}")
        return summary_df

def _add_zz_price_upper_limit(coupon_zz_filtered):
    """为转转优惠券补充价格上限字段"""
    try:
        # 根据价格下限计算价格上限
        def calculate_price_upper(price_min):
            if pd.isna(price_min):
                return 999999999
            
            price_min = int(price_min)
            if price_min < 1000:
                return 999999999
            elif price_min < 1500:
                return 1499
            elif price_min < 2000:
                return 1999
            elif price_min < 2500:
                return 2499
            elif price_min < 3000:
                return 2999
            else:
                return 999999999
        
        coupon_zz_filtered['price_range_max'] = coupon_zz_filtered['price_range_min'].apply(calculate_price_upper)
        return coupon_zz_filtered
        
    except Exception as e:
        logger.error(f"补充转转优惠券价格上限失败: {str(e)}")
        return coupon_zz_filtered

@st.cache_data(ttl=3600, show_spinner=False)  # 缓存1小时，禁用菊花
def _load_show_tables_data_cached():
    """预加载show_tables数据 - 使用缓存确保只加载一次"""
    try:
        logger.info("开始预加载show_tables数据...")
        
        # 创建临时的数据库管理器来加载数据
        config = AppConfig()
        db_manager = DatabaseManager(config)
        
        show_tables_data = {
            'ahs_coupons': None,
            'ahs_coupons_by_type': None,
            'zz_coupons': None,
            'price_competitiveness': None,
            'zz_price_chart': None,
            'last_updated': None
        }
        
        # 加载爱回收优惠券数据
        ahs_coupons_raw, ahs_coupons_by_type = _load_ahs_coupons_data_cached(db_manager)
        show_tables_data['ahs_coupons'] = ahs_coupons_raw
        show_tables_data['ahs_coupons_by_type'] = ahs_coupons_by_type
        
        # 加载转转优惠券数据
        show_tables_data['zz_coupons'] = _load_zz_coupons_data_cached(db_manager)
        
        # 加载价格竞争力数据
        show_tables_data['price_competitiveness'] = _load_price_competitiveness_data_cached()
        
        # 生成ZZ价格走势图
        show_tables_data['zz_price_chart'] = _generate_zz_price_chart_cached(db_manager)
        
        # 更新时间戳
        show_tables_data['last_updated'] = datetime.now()
        
        logger.info("show_tables数据预加载完成")
        return show_tables_data
        
    except Exception as e:
        logger.error(f"预加载show_tables数据失败: {str(e)}")
        return {
            'ahs_coupons': None,
            'zz_coupons': None,
            'price_competitiveness': None,
            'zz_price_chart': None,
            'last_updated': None
        }

@st.cache_data(ttl=3600, show_spinner=False)
def _load_ahs_coupons_data_cached(_db_manager):
    """加载爱回收优惠券数据 - 缓存版本，按coupon_type分组"""
    try:
        engine = _db_manager.get_engine()
        
        # 1. 加载用于"数据展示"的原始数据（保持原来的逻辑）
        ahs_coupon_sql = """
            select
                *
            from 
                ods.ods_c2babtrader_competitive_competitive_price_coupon_rule
            where category_id = 1
                and brand_id = 52
        """
        
        coupon_ahs = pd.read_sql(ahs_coupon_sql, engine)
        formatted_data = pd.DataFrame()
        
        if not coupon_ahs.empty:
            # 检查必要的列是否存在
            required_cols = ["package_code", "min_price_limit", "price"]
            missing_cols = [col for col in required_cols if col not in coupon_ahs.columns]
            if missing_cols:
                logger.warning(f"爱回收优惠券数据缺少列: {missing_cols}")
                formatted_data = coupon_ahs
            else:
                _, packages_by_signature = compute_unique_sets(coupon_ahs)
                summary_df = build_summary_df(packages_by_signature)
                # 格式化数据用于显示
                formatted_data = _format_ahs_coupon_data(summary_df)
        
        # 2. 加载用于"当前优惠券配置"的预设优惠券数据
        coupon_queries = {
            'SELF_1515': "select * from algo.ahs_self_coupon_1515",
            'SELF_1745': "select * from algo.ahs_self_coupon_1745", 
            'JD_2030': "select * from algo.ahs_jd_coupon_2030"
        }
        
        coupon_data_by_type = {}
        
        for coupon_type, sql_query in coupon_queries.items():
            try:
                coupon_data = pd.read_sql(sql_query, engine)
                if not coupon_data.empty:
                    # 确保数据类型正确
                    coupon_data['coupon_type'] = coupon_data['coupon_type'].astype(int)
                    coupon_data['price_range_min'] = coupon_data['price_range_min'].astype(int)
                    coupon_data['price_range_max'] = coupon_data['price_range_max'].astype(int)
                    coupon_data['coupon_price'] = coupon_data['coupon_price'].astype(int)
                    
                    # 按类型存储
                    coupon_data_by_type[coupon_type] = coupon_data
                    
                    logger.info(f"加载 {coupon_type} 优惠券数据成功: {len(coupon_data)} 条记录")
                else:
                    logger.warning(f"{coupon_type} 优惠券数据为空")
            except Exception as e:
                logger.error(f"加载 {coupon_type} 优惠券数据失败: {str(e)}")
        
        logger.info(f"爱回收优惠券数据加载成功: 数据展示 {len(formatted_data)} 条记录，预设优惠券 {len(coupon_data_by_type)} 种类型")
        return formatted_data, coupon_data_by_type
            
    except Exception as e:
        logger.error(f"加载爱回收优惠券数据失败: {str(e)}")
        return pd.DataFrame(), {}

@st.cache_data(ttl=3600, show_spinner=False)
def _load_zz_coupons_data_cached(_db_manager):
    """加载转转优惠券数据 - 缓存版本"""
    try:
        current_date_str = str(datetime.now().date())
        zz_coupon_sql = """
            select 
                * 
            from 
                rpt.rpt_platform_trade_price_compass_coupon_package_zz
            where 
                ahs_category_id = 1
                and create_date = date '{}'
        """.format(current_date_str)
        
        engine = _db_manager.get_engine()
        coupon_zz = pd.read_sql(zz_coupon_sql, engine)
        
        if not coupon_zz.empty:
            # 检查必要的列是否存在
            required_cols = ['coupon_type_name', 'price_range_min', 'price_range_max', 'coupon_price', 'coupon_desc', 'valid_time']
            available_cols = [col for col in required_cols if col in coupon_zz.columns]
            
            if len(available_cols) >= 3:  # 至少要有3个列
                coupon_zz_filtered = coupon_zz[available_cols].copy()
                
                # 补充价格上限字段（如果不存在或为空）
                if 'price_range_max' not in coupon_zz_filtered.columns or coupon_zz_filtered['price_range_max'].isna().all():
                    coupon_zz_filtered = _add_zz_price_upper_limit(coupon_zz_filtered)
                
                # 重命名列
                rename_dict = {
                    'coupon_type_name': '券类型',
                    'price_range_min': '价格下限',
                    'price_range_max': '价格上限',
                    'coupon_price': '优惠券额度',
                    'coupon_desc': '优惠券描述',
                    'valid_time': '优惠券有效期',
                }
                # 只重命名存在的列
                rename_dict = {k: v for k, v in rename_dict.items() if k in available_cols}
                coupon_zz_filtered.rename(columns=rename_dict, inplace=True)
                return coupon_zz_filtered
            else:
                # 如果列不完整，显示原始数据
                return coupon_zz
            logger.info(f"转转优惠券数据加载成功: {len(coupon_zz)} 条记录")
        else:
            logger.warning("转转优惠券数据为空")
            return pd.DataFrame()
            
    except Exception as e:
        logger.error(f"加载转转优惠券数据失败: {str(e)}")
        return pd.DataFrame()

@st.cache_data(ttl=3600, show_spinner=False)
def _load_price_competitiveness_data_cached():
    """加载价格竞争力数据 - 缓存版本"""
    try:
        # 获取当前日期的价格竞争力数据
        current_date_str = str(datetime.now().date())
        logger.info(f"尝试加载价格竞争力数据，日期: {current_date_str}")
        
        # 使用Excel文件路径加载数据
        excel_path = '/data_hdd/bryan/bi_strategy_today_iphone_g1/collections/chosen_result_g1_{}.xlsx'.format(current_date_str)
        logger.info(f"Excel文件路径: {excel_path}")
        
        # 检查文件是否存在
        import os
        if not os.path.exists(excel_path):
            logger.warning(f"价格竞争力Excel文件不存在: {excel_path}")
            
            # 尝试查找最近几天的文件
            for days_back in range(1, 8):  # 查找最近7天的文件
                past_date = datetime.now().date() - pd.Timedelta(days=days_back)
                past_date_str = str(past_date)
                past_excel_path = f'/data_hdd/bryan/bi_strategy_today_iphone_g1/collections/chosen_result_g1_{past_date_str}.xlsx'
                
                if os.path.exists(past_excel_path):
                    logger.info(f"找到最近的价格竞争力文件: {past_excel_path}")
                    excel_path = past_excel_path
                    break
            else:
                logger.warning("未找到任何价格竞争力Excel文件")
                return pd.DataFrame()
        
        price_data = pd.read_excel(excel_path)
        
        logger.info(f"价格竞争力Excel文件读取结果: {len(price_data)} 条记录")
        
        if not price_data.empty:
            logger.info(f"价格竞争力数据加载成功: {len(price_data)} 条记录")
            return price_data
        else:
            logger.warning("价格竞争力Excel文件为空")
            return pd.DataFrame()
            
    except Exception as e:
        logger.error(f"加载价格竞争力数据失败: {str(e)}")
        return pd.DataFrame()

@st.cache_data(ttl=3600, show_spinner=False)
def _generate_zz_price_chart_cached(_db_manager):
    """生成ZZ苹果手机不同ppv监控价格走势图 - 缓存版本"""
    try:
        
        # ZZ价格查询SQL
        zz_price_sql = text("""
            SELECT
                ppv1.activity_id,
                ppv1.brand,
                ppv1.product_name,
                base.product_sku_name,
                base.product_level_name,
                -- base.ppv,
                base.matched_zz_attributes,
                ppv1.start_price,
                ppv1.deal_price,
                ppv1.create_dt,
                ppv1.update_dt
            FROM 
                ods.ods_stage_fact_crawl_platform_electronics_goods_info ppv1
            JOIN
                -- algo.tmp_matched_zz_ppv base
                algo.tmp_matched_zz_ppv base
            ON ppv1.activity_id = base.activity_id
            WHERE 
                ppv1.from_web LIKE :pattern
                and ppv1.brand = '苹果'
                and date(ppv1.create_dt) >= current_date + interval '-14' day
        """)
        
        engine = _db_manager.get_engine()
        zz_67ppv_prices = pd.read_sql(zz_price_sql, engine, params={'pattern': '%test_zz_recycle_price%'})
        
        
        # 确保create_dt是datetime类型
        zz_67ppv_prices['create_dt'] = pd.to_datetime(zz_67ppv_prices['create_dt'])
        zz_67ppv_prices['update_dt'] = pd.to_datetime(zz_67ppv_prices['update_dt'])
        # 合并历史和新增数据
        zz_67ppv_prices['start_price'] = zz_67ppv_prices['start_price'].fillna(0)  # 防止没有爬到价格

        zz_price_iphone = zz_67ppv_prices
        zz_price_iphone['spider_time'] = pd.to_datetime(zz_price_iphone['create_dt'])
        zz_price_iphone['price'] = zz_price_iphone['start_price'].astype(int)
        zz_price_iphone = zz_price_iphone.sort_values(
            ['product_sku_name', 'product_level_name', 'spider_time']
        )
        
        fig, ax = plt.subplots(figsize=(16, 12))
        lines, labels = [], []

        # 文本标注向左偏移
        x_offset = pd.Timedelta(hours=2)

        for (sku, level), group in zz_price_iphone.groupby(['product_sku_name', 'product_level_name']):
            label = f"{sku} {level}"
            line, = ax.plot(
                group['spider_time'],
                group['price'],
                marker='o',
                markersize=4,
                markerfacecolor='none'
            )
            # 标记价格变化点（除首点外），用与线相同的颜色
            changes = group['price'].diff().fillna(0) != 0
            ax.scatter(
                group['spider_time'][changes],
                group['price'][changes],
                color=line.get_color(),
                s=60,
                marker='o'
            )

            # 标注变化前后两个点的价格
            for idx, changed in enumerate(changes.values):
                if not changed or idx == 0:
                    continue
                prev_time, prev_price = group['spider_time'].iloc[idx - 1], group['price'].iloc[idx - 1]
                cur_time, cur_price = group['spider_time'].iloc[idx], group['price'].iloc[idx]
                ax.text(prev_time, prev_price, f"{prev_price}", ha='center', va='bottom', fontsize=10)
                ax.text(cur_time, cur_price, f"{cur_price}", ha='center', va='bottom', fontsize=10)

            # 第一条数据左侧添加 product_name
            first_time, first_price = group['spider_time'].iloc[0], group['price'].iloc[0]
            product_name = group['product_name'].iloc[0]
            ax.text(
                first_time - x_offset, first_price,
                product_name,
                ha='right', va='center',
                fontsize=10
            )

            lines.append(line)
            labels.append(label)

        # 半透明虚线网格
        ax.grid(which='both', linestyle='--', linewidth=0.5, alpha=0.5)

        # 每日竖线
        dates = sorted(zz_price_iphone['spider_time'].dt.normalize().unique())
        for d in dates:
            ax.axvline(x=d, linestyle='-', linewidth=1.0, color='grey', alpha=1.0)

        # 次刻度（上方）和主刻度（下方）
        minor_ticks, minor_labels = [], []
        for d in dates:
            for h in [0, 6, 12, 18]:
                minor_ticks.append(d + pd.Timedelta(hours=h))
                minor_labels.append(str(h))
        ax.set_xticks(minor_ticks, minor=True)
        ax.set_xticklabels(minor_labels, minor=True)

        major_ticks = dates
        major_labels = [d.strftime('%Y-%m-%d') for d in dates]
        ax.set_xticks(major_ticks)
        ax.set_xticklabels(major_labels)

        ax.tick_params(axis='x', which='minor', labeltop=True, labelbottom=False, rotation=0)
        ax.tick_params(axis='x', which='major', labeltop=False, labelbottom=True, rotation=45)

        ax.set_ylabel('XX PPV Price (CNY)')

        # 图例放右侧
        ax.legend(
            lines, labels,
            loc='center left', bbox_to_anchor=(1.02, 0.5),
            frameon=False
        )
        fig.subplots_adjust(right=0.82)
        ax.set_title("XX苹果手机不同ppv监控价格走势")
        plt.tight_layout()
        
        # 保存为字节流
        buf = io.BytesIO()
        plt.savefig(buf, format='png', dpi=150, bbox_inches='tight')
        buf.seek(0)
        plt.close()
        
        logger.info("ZZ价格走势图生成成功")
        return buf.getvalue()
        
    except Exception as e:
        logger.error(f"生成ZZ价格走势图失败: {str(e)}")
        return None

class AppState:
    """应用状态管理"""
    _instance = None
    _show_tables_loaded = False
    _scheduler_started = False
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(AppState, cls).__new__(cls)
        return cls._instance
    
    def __init__(self):
        # 避免重复初始化
        if hasattr(self, '_initialized'):
            return
            
        self.config = AppConfig()
        self.db_manager = DatabaseManager(self.config)
        self.price_engine = PriceStrategyEngine(self.config)
        self.data_processor = DataProcessor(self.config)
        self.ui_components = UIComponents()
        
        # 初始化show_tables数据缓存
        self.show_tables_data = {
            'ahs_coupons': None,
            'zz_coupons': None,
            'price_competitiveness': None,
            'last_updated': None
        }
        
        # 初始化会话状态
        self._init_session_state()
        
        # 只在第一次创建实例时预加载show_tables数据
        if not AppState._show_tables_loaded:
            self._load_show_tables_data()
            AppState._show_tables_loaded = True
            logger.info("首次加载show_tables数据完成，后续将使用缓存数据")
        
        # 关闭定时更新任务（按需启用时再打开）
        if not AppState._scheduler_started:
            AppState._scheduler_started = True
            logger.info("定时更新任务已关闭（不再自动更新时间点）")
        
        self._initialized = True
    
    def _init_session_state(self):
        """初始化会话状态"""
        current_date = datetime.now().date()
        
        # 初始化所有必要的session_state变量（除了analysis_date，由date_input管理）
        session_vars = {
            'processing_results': {},
            'current_products': [],
            'current_date': current_date
        }
        
        for key, default_value in session_vars.items():
            if key not in st.session_state:
                st.session_state[key] = default_value
        
        # analysis_date由date_input组件管理，不在这里初始化
    
    def _load_show_tables_data(self):
        """预加载show_tables数据 - 调用缓存版本"""
        self.show_tables_data = _load_show_tables_data_cached()
        # 将数据存储到session_state中，供UI组件使用
        st.session_state.show_tables_data = self.show_tables_data
    
    def _start_scheduled_updates(self):
        """启动定时更新任务（已禁用）"""
        logger.info("定时更新任务未启动（已禁用）")
    
    def _update_zz_price_chart(self):
        """更新ZZ价格走势图"""
        try:
            logger.info("开始定时更新ZZ价格走势图...")
            
            # 清除缓存，强制重新生成ZZ价格走势图
            _generate_zz_price_chart_cached.clear()
            logger.info("已清除ZZ价格走势图缓存，开始重新生成...")
            
            # 重新生成ZZ价格走势图
            new_chart = _generate_zz_price_chart_cached(self.db_manager)
            
            if new_chart is not None:
                # 更新缓存数据
                self.show_tables_data['zz_price_chart'] = new_chart
                
                # 更新session_state中的数据
                if hasattr(st.session_state, 'show_tables_data'):
                    st.session_state.show_tables_data['zz_price_chart'] = new_chart
                
                # 设置自动刷新标志，触发界面更新
                st.session_state.zz_chart_updated = True
                st.session_state.zz_chart_update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                
                logger.info("ZZ价格走势图定时更新成功")
            else:
                logger.warning("ZZ价格走势图定时更新失败：生成图表为空")
                
        except Exception as e:
            logger.error(f"定时更新ZZ价格走势图失败: {str(e)}")
    
    def check_and_clear_zz_chart_update_flag(self):
        """检查并清除ZZ图表更新标志"""
        if hasattr(st.session_state, 'zz_chart_updated') and st.session_state.zz_chart_updated:
            st.session_state.zz_chart_updated = False
            return True
        return False
    
    def _load_ahs_coupons_data(self):
        """加载爱回收优惠券数据"""
        self.show_tables_data['ahs_coupons'] = _load_ahs_coupons_data_cached(self.db_manager)

    
    def _load_zz_coupons_data(self):
        """加载转转优惠券数据"""
        self.show_tables_data['zz_coupons'] = _load_zz_coupons_data_cached(self.db_manager)

    def _load_price_competitiveness_data(self):
        """加载价格竞争力数据"""
        self.show_tables_data['price_competitiveness'] = _load_price_competitiveness_data_cached()
    
    def normalize_series_to_fraction(self, s: pd.Series):
        """
        把序列统一到 [0,1] 小数：
        - 若最大绝对值 > 1.5，判定源是 0~100 百分数 → /100
        - 否则认为已是 0~1 小数，原样返回
        - 非数值转为 NaN
        """
        s = pd.to_numeric(s, errors="coerce")
        if s.dropna().empty:
            return s
        max_abs = s.dropna().abs().max()
        return s / 100.0 if max_abs > 1.5 else s
    
    def _render_bar_cell(self, val, color, fmt="0.0%", width_px=120, height_px=12):
        """
        单元格：数据条 + 文本。
        - 文本：使用原始值（可 > 100%）
        - 条形：仅用于可视化，宽度截断到 [0,1]，避免溢出
        fmt 支持 '0%'（整数百分比）或 '0.0%'（一位小数）
        """
        if pd.isna(val):
            return f"""<div style="width:{width_px}px;margin:0 auto;">
                <div style="width:{width_px}px;height:{height_px}px;background:#f3f3f3;border-radius:2px;"></div>
                <div style="font-size:12px;color:#666;margin-top:2px;">-</div></div>"""

        v_raw = float(val)                  # 原始值（可能 > 1.0）
        v_bar = max(0.0, min(v_raw, 1.0))   # 仅用于条形宽度

        # 文本格式
        if fmt == "0%":
            txt = f"{v_raw * 100:.0f}%"
        else:
            txt = f"{v_raw:.1%}"

        w = f"{v_bar * 100:.1f}%"

        return f"""<div style="width:{width_px}px;margin:0 auto;">
            <div style="width:{width_px}px;height:{height_px}px;background:#f3f3f3;border-radius:2px;">
                <div style="width:{w};height:{height_px}px;background:{color};border-radius:2px;"></div>
            </div>
            <div style="font-size:12px;color:#333;margin-top:2px;">{txt}</div>
        </div>"""
    
    def _iphone_generation(self, name: str):
        """提取 iPhone 代数（12/13/14/15...）；失败返回极大值以置后。"""
        m = re.search(r"iPhone\s*(\d+)", str(name), re.I)
        return int(m.group(1)) if m else 10**9

    def _is_pro_max(self, name: str):
        return re.search(r"Pro\s*Max", str(name), re.I) is not None

    def _is_pro_only(self, name: str):
        return (re.search(r"\bPro\b", str(name), re.I) is not None) and (not self._is_pro_max(name))

    def preferred_iphone_order(self, product_names):
        """
        自动生成顺序：按代数升序分组；同代内 标准款 -> Pro -> Pro Max。
        仅返回传入列表中存在的型号（保持不重复）。
        """
        names = list(dict.fromkeys(product_names))
        buckets: dict[int, list[str]] = {}
        for n in names:
            buckets.setdefault(self._iphone_generation(n), []).append(n)

        result = []
        for gen in sorted(buckets.keys()):
            prods = buckets[gen]
            # 同代内排序：标准款 -> Pro -> Pro Max
            prods.sort(key=lambda x: (
                0 if not (self._is_pro_only(x) or self._is_pro_max(x)) else
                1 if self._is_pro_only(x) else 2
            ))
            result.extend(prods)
        return result
    
    def build_price_email_html(self, df: pd.DataFrame,
        metric_order = ("bi_ratio", "inquiry_compete_rate", "cost_rate_estimated"),
        metric_labels = {"bi_ratio": "BI价距", "inquiry_compete_rate": "竞争力", "cost_rate_estimated": "费用率"},
        metric_colors = {"bi_ratio": "#F4B183", "inquiry_compete_rate": "#9BBB59", "cost_rate_estimated": "#E57373"},
        metric_formats = {"bi_ratio": "0%", "inquiry_compete_rate": "0.0%", "cost_rate_estimated": "0.0%"},
        target_format = "0%",              # target 按 55% 效果展示（整数百分比）
        product_order= None
    ):
        """
        参数：
            df: 含列 product_name, target, inquiry_compete_rate, cost_rate_estimated, bi_ratio
            product_order: 自定义型号顺序（仅保留 df 中存在的型号），不传则自动按代数 + Pro 序

        返回：
            可直接嵌入邮件正文的 <table> HTML 字符串
        """
        required = {"product_name", "target", "inquiry_compete_rate", "cost_rate_estimated", "bi_ratio"}
        miss = required - set(df.columns)
        if miss:
            raise ValueError(f"缺少必要列：{miss}")

        df = df.copy()

        # 统一数值到 [0,1] 小数
        for c in ["target", "inquiry_compete_rate", "cost_rate_estimated", "bi_ratio"]:
            df[c] = self.normalize_series_to_fraction(df[c])

        # 行顺序（target）：优先 0~0.95（步长 0.05），否则按实际唯一值升序
        uniq_targets = sorted(df["target"].dropna().unique().tolist())
        canonical = [round(x, 4) for x in np.arange(0.0, 0.951, 0.05)]
        row_index = canonical if len(set(np.round(uniq_targets, 4)).intersection(set(np.round(canonical, 4)))) >= max(8, int(0.6 * len(canonical))) \
                    else uniq_targets

        # 列顺序（product）：使用自定义顺序或自动顺序（仅保留 df 中存在的）
        if product_order is None:
            product_order = self.preferred_iphone_order(df["product_name"].dropna().drop_duplicates().tolist())
        else:
            present = set(df["product_name"].dropna().unique().tolist())
            product_order = [p for p in product_order if p in present]

        # 透视：(metric, product) → 调整成 (product, metric) 便于遍历
        agg_df = df.groupby(["target", "product_name"], as_index=False)[list(metric_order)].mean(numeric_only=True)
        pivot = agg_df.pivot(index="target", columns="product_name", values=list(metric_order)).reindex(index=row_index)
        pivot = pivot.reindex(columns=pd.MultiIndex.from_product([metric_order, product_order]))
        pivot = pivot.swaplevel(0, 1, axis=1)  # -> (product, metric)

        # ===== 生成 HTML =====
        html = []
        html.append('<table style="width:98%;border-collapse:collapse;margin:12px auto;font-family:Arial,Helvetica,sans-serif;">')

        # 表头-行1：左上角为"型号"；每个型号跨 3 列
        html.append('<tr>')
        html.append('<th style="border:1px solid #ddd;padding:8px;background:#edf3fe;">型号</th>')
        for prod in product_order:
            html.append(f'<th colspan="{len(metric_order)}" style="border:1px solid #ddd;padding:8px;background:#f2f2f2;font-weight:bold;">{prod}</th>')
        html.append('</tr>')

        # 表头-行2：左侧写 target；右侧写指标名
        html.append('<tr>')
        html.append('<th style="border:1px solid #ddd;padding:8px;background:#fafafa;">target</th>')
        for prod in product_order:
            for m in metric_order:
                label = metric_labels.get(m, m)
                html.append(f'<th style="border:1px solid #ddd;padding:8px;background:#fafafa;">{label}</th>')
        html.append('</tr>')

        # 数据行
        for t in row_index:
            if pd.isna(t):
                t_txt = "-"
            else:
                t_txt = f"{float(t) * 100:.0f}%" if target_format == "0%" else f"{float(t):.1%}"

            html.append('<tr>')
            # 左侧 target 列（背景浅蓝）
            html.append(f'<td style="border:1px solid #ddd;padding:8px;text-align:center;background:#edf3fe;font-weight:bold;">{t_txt}</td>')

            # 指标格子（数据条 + 数值）
            for prod in product_order:
                for m in metric_order:
                    color = metric_colors.get(m, "#999")
                    fmt   = metric_formats.get(m, "0.0%")
                    val = pivot.loc[t, (prod, m)] if (prod, m) in pivot.columns else np.nan
                    cell_html = self._render_bar_cell(val, color, fmt=fmt)
                    html.append(f'<td style="border:1px solid #ddd;padding:6px;text-align:center;">{cell_html}</td>')
            html.append('</tr>')

        html.append('</table>')
        return "".join(html)
    


class NewPriceStrategyApp:
    """新版价格策略应用主类"""
    
    def __init__(self):
        self.state = AppState()
        self.logger = logger
        self.strategy_selector = PriceStrategySelector(llm_provider="qwen")
        
    def render_header(self):
        """渲染页面头部"""
        st.markdown('<h1 class="main-header">智能打"XX"侠</h1>', unsafe_allow_html=True)
        st.markdown("---")
    
    def render_sidebar(self):
        """渲染侧边栏"""
        with st.sidebar:
            st.header("🎛️ 控制面板")
            
            # 产品类别选择
            category = st.selectbox(
                "选择产品类别",
                ["手机", "平板", "智能手表", "耳机", "笔记本"],
                key="product_category"
            )
            
            # 品牌选择（如果是手机）
            brand = None
            if category == "手机":
                brand = st.radio(
                    "选择品牌",
                    ["苹果", "安卓", "全部"],
                    key="phone_brand"
                )
            
            # 日期选择
            # 获取默认日期，如果session_state中没有则使用当前日期
            default_date = getattr(st.session_state, 'analysis_date', datetime.now().date())
            
            selected_date = st.date_input(
                "选择分析日期",
                value=default_date,
                max_value=datetime.now().date(),
                key="analysis_date"
            )
            
            # 注意：不需要手动更新session_state，date_input会自动更新
            
            # 高级设置
            with st.expander("⚙️ 高级设置"):
                max_workers = st.slider("并行处理数", 1, 16, 8)
                timeout = st.slider("超时时间(秒)", 30, 600, 300)
                debug_mode = st.checkbox("调试模式", False)
            
            return {
                'category': category,
                'brand': brand,
                'date': selected_date,
                'max_workers': max_workers,
                'timeout': timeout,
                'debug_mode': debug_mode
            }
    
    def _display_selected_coupon(self, coupon_type: str, custom_coupon_data: Optional[list]):
        """展示当前选择的优惠券内容"""
        st.subheader("📋 当前优惠券配置")
        
        if coupon_type == "customed" and custom_coupon_data:
            # 展示自定义优惠券
            st.write("**自定义优惠券配置：**")
            
            # 创建表格数据
            coupon_df = pd.DataFrame(custom_coupon_data)
            coupon_df = coupon_df[['price_range_min', 'price_range_max', 'coupon_price']].copy()
            coupon_df.columns = ['价格下限', '价格上限', '优惠券额度']
            
            # 格式化显示
            st.dataframe(
                coupon_df,
                hide_index=True
            )
            
        else:
            # 展示预设优惠券
            try:
                # 从预加载的数据中获取优惠券数据
                if hasattr(st.session_state, 'show_tables_data') and st.session_state.show_tables_data:
                    ahs_coupons_by_type = st.session_state.show_tables_data.get('ahs_coupons_by_type', {})
                    
                    if coupon_type in ["SELF_1515", "SELF_1745", "JD_2030"]:
                        # 从预加载的数据中获取对应类型的优惠券
                        if coupon_type in ahs_coupons_by_type:
                            coupon_data = ahs_coupons_by_type[coupon_type]
                            
                            if coupon_data is not None and not coupon_data.empty:
                                st.write(f"**预设优惠券配置 ({coupon_type})：**")
                                
                                # 格式化显示 - 使用原始字段名
                                display_df = coupon_data[['price_range_min', 'price_range_max', 'coupon_price']].copy()
                                display_df.columns = ['价格下限', '价格上限', '优惠券额度']
                                
                                # 添加Package Codes列
                                display_df['Package Codes'] = coupon_type
                                
                                st.dataframe(
                                    display_df,
                                    hide_index=True
                                )
                            else:
                                st.warning(f"未找到 {coupon_type} 类型的优惠券数据")
                        else:
                            st.warning(f"未找到 {coupon_type} 类型的优惠券数据")
                    else:
                        st.info("请选择优惠券类型")
                else:
                    st.warning("优惠券数据未预加载，请刷新页面重试")
                    
            except Exception as e:
                logger.error(f"展示优惠券内容失败: {str(e)}")
                st.error(f"展示优惠券内容失败: {str(e)}")
                # 添加调试信息
                st.write("**调试信息:**")
                st.write(f"- 优惠券类型: {coupon_type}")
                st.write(f"- 自定义数据: {custom_coupon_data is not None}")
                if custom_coupon_data:
                    st.write(f"- 自定义数据长度: {len(custom_coupon_data)}")
                if hasattr(st.session_state, 'show_tables_data') and st.session_state.show_tables_data:
                    ahs_coupons_by_type = st.session_state.show_tables_data.get('ahs_coupons_by_type', {})
                    st.write(f"- 预加载的优惠券类型: {list(ahs_coupons_by_type.keys())}")
    
    def render_product_selection(self, category: str, brand: Optional[str] = None):
        """渲染产品选择界面"""
        # AHS优惠券选择（在产品选择上方）
        st.header("🎫 AHS优惠券设置")
        coupon_type = st.selectbox(
            "选择优惠券类型",
            ["SELF_1515", "SELF_1745", "JD_2030", "customed"],
            index=0,  # 默认选择SELF_1515
            key="ahs_coupon_type",
            help="选择预设优惠券类型或自定义优惠券"
        )
        
        # 如果选择自定义优惠券，显示自定义输入界面
        custom_coupon_data = None
        if coupon_type == "customed":
            st.write("**自定义优惠券设置**")
            st.write("请填写各价格区间的优惠券额度：")
            
            # 创建自定义优惠券输入界面
            col1, col2 = st.columns(2)
            
            with col1:
                coupon_200_399 = st.number_input("200-399元区间优惠券", min_value=0, max_value=999, value=40, key="coupon_200_399")
                coupon_400_799 = st.number_input("400-799元区间优惠券", min_value=0, max_value=999, value=70, key="coupon_400_799")
                coupon_800_1199 = st.number_input("800-1199元区间优惠券", min_value=0, max_value=999, value=130, key="coupon_800_1199")
                coupon_1200_1999 = st.number_input("1200-1999元区间优惠券", min_value=0, max_value=999, value=200, key="coupon_1200_1999")
            
            with col2:
                coupon_2000_2999 = st.number_input("2000-2999元区间优惠券", min_value=0, max_value=999, value=300, key="coupon_2000_2999")
                coupon_3000_3999 = st.number_input("3000-3999元区间优惠券", min_value=0, max_value=999, value=380, key="coupon_3000_3999")
                coupon_4000_4799 = st.number_input("4000-4799元区间优惠券", min_value=0, max_value=999, value=430, key="coupon_4000_4799")
                coupon_4800_plus = st.number_input("4800元以上区间优惠券", min_value=0, max_value=999, value=480, key="coupon_4800_plus")
            
            # 构建自定义优惠券数据
            custom_coupon_data = [
                {"coupon_type": 0, "price_range_operator": "between", "price_range_min": 200, "price_range_max": 399, "coupon_price": coupon_200_399},
                {"coupon_type": 0, "price_range_operator": "between", "price_range_min": 400, "price_range_max": 799, "coupon_price": coupon_400_799},
                {"coupon_type": 0, "price_range_operator": "between", "price_range_min": 800, "price_range_max": 1199, "coupon_price": coupon_800_1199},
                {"coupon_type": 0, "price_range_operator": "between", "price_range_min": 1200, "price_range_max": 1999, "coupon_price": coupon_1200_1999},
                {"coupon_type": 0, "price_range_operator": "between", "price_range_min": 2000, "price_range_max": 2999, "coupon_price": coupon_2000_2999},
                {"coupon_type": 0, "price_range_operator": "between", "price_range_min": 3000, "price_range_max": 3999, "coupon_price": coupon_3000_3999},
                {"coupon_type": 0, "price_range_operator": "between", "price_range_min": 4000, "price_range_max": 4799, "coupon_price": coupon_4000_4799},
                {"coupon_type": 0, "price_range_operator": "between", "price_range_min": 4800, "price_range_max": 99999999, "coupon_price": coupon_4800_plus}
            ]
        
        # 将优惠券设置存储到session_state中
        st.session_state.coupon_type = coupon_type
        st.session_state.custom_coupon_data = custom_coupon_data
        
        # 展示当前选择的优惠券内容
        self._display_selected_coupon(coupon_type, custom_coupon_data)
        
        st.header("📱 产品选择")
        
        # 获取产品列表
        products = self.state.config.get_products(category, brand)
        
        if not products:
            st.warning("未找到匹配的产品")
            return []
        
        # 产品选择界面
        col1, col2 = st.columns([2, 1])
        
        with col1:
            # 全选/取消全选
            select_all = st.checkbox("全选产品", key="select_all_products")
            
            if select_all:
                selected_products = products
            else:
                selected_products = st.multiselect(
                    "选择要分析的产品",
                    products,
                    default=[],
                    key="selected_products"
                )
        
        with col2:
            st.metric("已选择", len(selected_products))
            if selected_products:
                st.write("**已选择的产品:**")
                for product in selected_products[:5]:  # 只显示前5个
                    st.write(f"• {product}")
                if len(selected_products) > 5:
                    st.write(f"... 还有 {len(selected_products) - 5} 个产品")
        
        return selected_products
    
    def render_competitiveness_settings(self, products: List[str]):
        """渲染竞争力设置界面"""
        st.header("🎯 竞争力设置")
        
        if not products:
            st.info("请先选择产品")
            return {}
        
        # 竞争力设置选项
        setting_type = st.radio(
            "竞争力设置方式",
            ["统一设置", "分别设置"],
            key="competitiveness_setting_type"
        )
        
        competitiveness_dict = {}
        
        if setting_type == "统一设置":
            # 统一竞争力设置
            common_competitiveness = st.slider(
                "所有产品的目标竞争力 (%)",
                min_value=0,
                max_value=100,
                value=50,
                step=1,
                key="common_competitiveness"
            ) / 100.0
            
            competitiveness_dict = {product: common_competitiveness for product in products}
            
            st.info(f"所有产品将使用 {common_competitiveness:.1%} 的目标竞争力")
        
        else:
            # 分别设置竞争力
            st.write("为每个产品单独设置竞争力:")
            
            # 使用两列布局
            cols = st.columns(2)
            for i, product in enumerate(products):
                with cols[i % 2]:
                    competitiveness = st.slider(
                        f"{product}",
                        min_value=0,
                        max_value=100,
                        value=50,
                        step=1,
                        key=f"competitiveness_{product}"
                    ) / 100.0
                    competitiveness_dict[product] = competitiveness
        
        return competitiveness_dict
    
    def render_processing_controls(self, products: List[str], competitiveness_dict: Dict[str, float], settings: Dict):
        """渲染处理控制界面"""
        st.header("🚀 开始分析")
        
        if not products:
            st.warning("请先选择产品")
            return
        
        if not competitiveness_dict:
            st.warning("请先设置竞争力参数")
            return
        
        # 显示处理摘要
        st.subheader("处理摘要")
        col1, col2, col3 = st.columns(3)
        
        with col1:
            st.metric("产品数量", len(products))
        with col2:
            avg_competitiveness = np.mean(list(competitiveness_dict.values()))
            st.metric("平均竞争力", f"{avg_competitiveness:.1%}")
        with col3:
            st.metric("预计耗时", f"{len(products) * 2:.0f}秒")
        
        # 开始处理按钮
        if st.button("🚀 开始价格策略分析", type="primary"):
            self.run_analysis(products, competitiveness_dict, settings)
    
    def run_analysis(self, products: List[str], competitiveness_dict: Dict[str, float], settings: Dict):
        """运行价格策略分析"""
        try:
            # 创建进度条和状态显示
            progress_bar = st.progress(0)
            status_text = st.empty()
            results_container = st.container()
            
            # 初始化结果存储
            results = {}
            completed = 0
            total = len(products)
            
            # 显示开始信息
            status_text.text(f"开始处理 {total} 个产品...")
            
            # 获取目标日期
            target_date = getattr(st.session_state, 'analysis_date', datetime.now().date())
            
            # 获取优惠券设置
            coupon_type = getattr(st.session_state, 'coupon_type', 'SELF_1515')
            custom_coupon_data = getattr(st.session_state, 'custom_coupon_data', None)
            
            # 并行处理产品
            with concurrent.futures.ThreadPoolExecutor(max_workers=settings['max_workers']) as executor:
                # 提交所有任务，传递日期参数和优惠券设置
                future_to_product = {
                    executor.submit(self.process_single_product, product, competitiveness_dict[product], target_date, coupon_type, custom_coupon_data): product
                    for product in products
                }
                
                # 处理完成的任务
                for future in concurrent.futures.as_completed(future_to_product):
                    product = future_to_product[future]
                    try:
                        result = future.result()
                        results[product] = result
                        completed += 1
                        
                        # 更新进度
                        progress = completed / total
                        progress_bar.progress(progress)
                        status_text.text(f"已完成 {completed}/{total} 个产品...")
                        
                        # 显示实时结果
                        if result.success:
                            st.success(f"✅ {product}: {result.message}")
                        else:
                            st.error(f"❌ {product}: {result.message}")
                            
                    except Exception as e:
                        logger.error(f"处理产品 {product} 时出错: {str(e)}")
                        results[product] = ProcessingResult(
                            success=False,
                            message=f"处理出错: {str(e)}",
                            error_details=str(e)
                        )
                        completed += 1
                        progress_bar.progress(completed / total)
            
            # 处理完成
            progress_bar.progress(1.0)
            status_text.text("所有产品处理完成!")
            
            # 保存结果到会话状态
            st.session_state.processing_results = results
            st.session_state.current_products = products
            
            # 显示结果摘要
            self.display_results_summary(results)
            
        except Exception as e:
            logger.error(f"分析过程中出错: {str(e)}")
            st.error(f"分析失败: {str(e)}")
    
    def process_single_product(self, product: str, competitiveness: float, target_date=None, coupon_tag='SELF_1515', custom_coupon_data=None) -> ProcessingResult:
        """处理单个产品"""
        start_time = datetime.now()
        
        try:
            logger.info(f"开始处理产品: {product}, 竞争力: {competitiveness}")
            
            # 获取目标日期，优先使用传入的参数，否则从session_state获取
            if target_date is None:
                target_date = getattr(st.session_state, 'analysis_date', datetime.now().date())
            
            result = self.state.price_engine.calculate_strategy(
                product_name=product,
                target=competitiveness,
                target_date=target_date,
                coupon_tag=coupon_tag,
                custom_coupon_data=custom_coupon_data
            )
            
            processing_time = (datetime.now() - start_time).total_seconds()
            
            # 添加调试信息
            data = result.get('data')
            sku_level_df = result.get('sku_level_df')
            selected_chosen_results = result.get('selected_chosen_results')
            
            logger.info(f"价格引擎返回结果: success={result.get('success')}, data_type={type(data)}")
            if data is not None:
                logger.info(f"data形状: {data.shape}, 列数: {len(data.columns) if hasattr(data, 'columns') else 'N/A'}")
                logger.info(f"data列名: {list(data.columns) if hasattr(data, 'columns') else 'N/A'}")
            else:
                logger.warning("价格引擎返回的data为None")
            
            if sku_level_df is not None:
                logger.info(f"sku_level_df形状: {sku_level_df.shape}, 列数: {len(sku_level_df.columns) if hasattr(sku_level_df, 'columns') else 'N/A'}")
            else:
                logger.warning("价格引擎返回的sku_level_df为None")
            
            if selected_chosen_results is not None:
                logger.info(f"selected_chosen_results形状: {selected_chosen_results.shape}, 列数: {len(selected_chosen_results.columns) if hasattr(selected_chosen_results, 'columns') else 'N/A'}")
            else:
                logger.warning("价格引擎返回的selected_chosen_results为None")
            
            return ProcessingResult(
                success=True,
                data=data,  # chosen_df
                sku_level_df=result.get('sku_level_df'),
                selected_chosen_results=result.get('selected_chosen_results'),
                message=f"处理成功 (耗时: {processing_time:.2f}秒)",
                processing_time=processing_time
            )
            
        except Exception as e:
            processing_time = (datetime.now() - start_time).total_seconds()
            logger.error(f"处理产品 {product} 时出错: {str(e)}")
            
            return ProcessingResult(
                success=False,
                message=f"处理失败: {str(e)}",
                processing_time=processing_time,
                error_details=str(e)
            )
    
    def display_results_summary(self, results: Dict[str, ProcessingResult]):
        """显示结果摘要"""
        st.header("📊 分析结果")
        
        # 统计信息
        successful = sum(1 for r in results.values() if r.success)
        failed = len(results) - successful
        total_time = sum(r.processing_time for r in results.values())
        
        col1, col2, col3, col4 = st.columns(4)
        with col1:
            st.metric("成功", successful, delta=None)
        with col2:
            st.metric("失败", failed, delta=None)
        with col3:
            st.metric("总耗时", f"{total_time:.1f}秒")
        with col4:
            avg_time = total_time / len(results) if results else 0
            st.metric("平均耗时", f"{avg_time:.1f}秒")
        
        # 详细结果展示
        if successful > 0:
            st.subheader("✅ 成功处理的产品")
            successful_results = {k: v for k, v in results.items() if v.success}
            
            # 创建选项卡展示不同类型的数据
            tabs = st.tabs(["策略摘要", "SKU-LEVEL价格明细", "PPV价格结果明细"])
            
            with tabs[0]:
                self.display_strategy_summary(successful_results)
            
            with tabs[1]:
                self.display_detailed_data(successful_results)
            
            with tabs[2]:
                self.display_ppv_results(successful_results)
        
        if failed > 0:
            st.subheader("❌ 处理失败的产品")
            failed_results = {k: v for k, v in results.items() if not v.success}
            
            for product, result in failed_results.items():
                with st.expander(f"❌ {product}"):
                    st.error(result.message)
                    if result.error_details:
                        st.code(result.error_details, language="python")
    
    def display_strategy_summary(self, results: Dict[str, ProcessingResult]):
        """显示策略摘要"""
        # 添加调试信息
        st.write("**调试信息 - 策略摘要数据检查**:")
        for product, result in results.items():
            st.write(f"- {product}: success={result.success}, data_type={type(result.data)}, data_empty={result.data is None or result.data.empty if result.data is not None else 'N/A'}")
            if result.data is not None and not result.data.empty:
                st.write(f"  - 数据形状: {result.data.shape}")
                st.write(f"  - 列名: {list(result.data.columns)}")
        
        # 合并所有产品的chosen_df数据
        all_data = []
        for product, result in results.items():
            if result.data is not None and not result.data.empty:
                all_data.append(result.data)
        
        if all_data:
            # 合并所有数据
            combined_data = pd.concat(all_data, ignore_index=True)
            
            # 按product_name排序
            if 'product_name' in combined_data.columns:
                combined_data = combined_data.sort_values('product_name', ascending=True).reset_index(drop=True)
            
            # 定义前置字段
            priority_fields = ['partition_date', 'brand_name', 'product_name', 'target', 'bi_ratio', 'cost_rate_estimated', 'inquiry_compete_rate']
            
            # 获取所有列名
            all_columns = list(combined_data.columns)
            
            # 分离前置字段和其他字段
            front_columns = [col for col in priority_fields if col in all_columns]
            other_columns = [col for col in all_columns if col not in priority_fields]
            
            # 重新排列列顺序
            reordered_columns = front_columns + other_columns
            combined_data = combined_data[reordered_columns]
            
            # 格式化百分比字段
            percentage_fields = ['target', 'bi_ratio', 'cost_rate_estimated', 'inquiry_compete_rate']
            for field in percentage_fields:
                if field in combined_data.columns:
                    # 将小数转换为百分比格式
                    combined_data[field] = combined_data[field].apply(lambda x: f"{float(x)*100:.0f}%" if pd.notna(x) else "")
            
            st.write("**策略摘要**:")
            st.dataframe(combined_data)
            
            # 提供Excel下载功能
            self._download_excel(combined_data, "策略摘要", "strategy_summary")
        else:
            st.warning("暂无数据可显示")
    
    def display_detailed_data(self, results: Dict[str, ProcessingResult]):
        """显示SKU-LEVEL价格明细"""
        # 添加调试信息
        st.write("**调试信息 - SKU-LEVEL数据检查**:")
        for product, result in results.items():
            st.write(f"- {product}: sku_level_df_type={type(result.sku_level_df)}, sku_level_df_empty={result.sku_level_df is None or result.sku_level_df.empty if result.sku_level_df is not None else 'N/A'}")
            if result.sku_level_df is not None and not result.sku_level_df.empty:
                st.write(f"  - 数据形状: {result.sku_level_df.shape}")
                st.write(f"  - 列名: {list(result.sku_level_df.columns)}")
        
        # 合并所有产品的sku_level_df数据
        all_data = []
        for product, result in results.items():
            if result.sku_level_df is not None and not result.sku_level_df.empty:
                all_data.append(result.sku_level_df)
        
        if all_data:
            # 合并所有数据
            combined_data = pd.concat(all_data, ignore_index=True)
            
            # 显示数据维度
            st.write(f"**数据维度**: {combined_data.shape[0]} 行 × {combined_data.shape[1]} 列")
            
            # 显示列信息
            st.write("**列信息**:")
            col_info = pd.DataFrame({
                '列名': combined_data.columns,
                '数据类型': combined_data.dtypes.astype(str),  # 转换为字符串类型
                '非空值数量': combined_data.count(),
                '空值数量': combined_data.isnull().sum()
            })
            st.dataframe(col_info)
            
            # 显示完整数据表格
            st.write("**SKU-LEVEL价格明细**:")
            st.dataframe(combined_data)
            
            # 提供Excel下载功能
            self._download_excel(combined_data, "SKU-LEVEL价格明细", "sku_level_details")
        else:
            st.warning("暂无SKU-LEVEL数据可显示")
    
    def display_ppv_results(self, results: Dict[str, ProcessingResult]):
        """显示PPV价格结果明细"""
        # 添加调试信息
        st.write("**调试信息 - PPV结果数据检查**:")
        for product, result in results.items():
            st.write(f"- {product}: selected_chosen_results_type={type(result.selected_chosen_results)}, selected_chosen_results_empty={result.selected_chosen_results is None or result.selected_chosen_results.empty if result.selected_chosen_results is not None else 'N/A'}")
            if result.selected_chosen_results is not None and not result.selected_chosen_results.empty:
                st.write(f"  - 数据形状: {result.selected_chosen_results.shape}")
                st.write(f"  - 列名: {list(result.selected_chosen_results.columns)}")
        
        # 合并所有产品的selected_chosen_results数据
        all_data = []
        for product, result in results.items():
            if result.selected_chosen_results is not None and not result.selected_chosen_results.empty:
                all_data.append(result.selected_chosen_results)
        
        if all_data:
            # 合并所有数据
            combined_data = pd.concat(all_data, ignore_index=True)
            
            # 显示数据维度
            st.write(f"**数据维度**: {combined_data.shape[0]} 行 × {combined_data.shape[1]} 列")
            
            # 显示列信息
            st.write("**列信息**:")
            col_info = pd.DataFrame({
                '列名': combined_data.columns,
                '数据类型': combined_data.dtypes.astype(str),  # 转换为字符串类型
                '非空值数量': combined_data.count(),
                '空值数量': combined_data.isnull().sum()
            })
            st.dataframe(col_info)
            
            # 显示完整数据表格
            st.write("**PPV价格结果明细**:")
            st.dataframe(combined_data)
            
            # 提供Excel下载功能
            self._download_excel(combined_data, "PPV价格结果明细", "ppv_results")
        else:
            st.warning("暂无PPV价格结果数据可显示")
    
    def _download_excel(self, data: pd.DataFrame, title: str, file_prefix: str):
        """提供Excel下载功能，支持中文"""
        try:
            # 创建Excel文件
            output = io.BytesIO()
            with pd.ExcelWriter(output, engine='openpyxl') as writer:
                data.to_excel(writer, sheet_name='数据', index=False)
            
            # 获取Excel文件内容
            excel_data = output.getvalue()
            
            # 生成文件名
            current_date = datetime.now().strftime("%Y%m%d")
            file_name = f"{file_prefix}_{current_date}.xlsx"
            
            # 提供下载按钮
            st.download_button(
                label=f"📥 下载{title}",
                data=excel_data,
                file_name=file_name,
                mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                key=f"download_{file_prefix}_{hash(str(data))}"
            )
        except Exception as e:
            logger.error(f"生成Excel文件失败: {str(e)}")
            st.error(f"生成Excel文件失败: {str(e)}")
    
    def display_show_tables_data(self):
        """显示show_tables.py中的数据"""
        try:
            st.subheader("📊 数据展示")
            
            # 首先显示ZZ价格走势图
            self.display_zz_price_chart()
            
            # 然后显示优惠券选项卡
            tabs = st.tabs(["爱回收优惠券", "XX优惠券"])
            
            with tabs[0]:
                self.display_ahs_coupons()
            
            with tabs[1]:
                self.display_zz_coupons()
            
            # 价格竞争力单独显示在优惠券下方
            st.markdown("---")  # 分隔线
            self.display_price_competitiveness()
            
            # 智能策略推荐显示在最下面
            st.markdown("---")  # 分隔线
            self.display_strategy_recommendations()
                
        except Exception as e:
            logger.error(f"显示show_tables数据时出错: {str(e)}")
            st.error(f"显示数据时出错: {str(e)}")
    
    def display_zz_price_chart(self):
        """显示XX苹果手机不同ppv监控价格走势图"""
        try:
            # 创建标题和更新按钮的布局
            col1, col2 = st.columns([3, 1])
            
            with col1:
                st.write("**📈 XX苹果手机不同ppv监控价格走势**")
            
            with col2:
                if st.button("🔄 手动更新", key="manual_update_zz_chart", help="点击更新ZZ价格走势图数据"):
                    # 手动更新ZZ价格走势图
                    with st.spinner("正在更新XX价格走势图..."):
                        try:
                            # 清除缓存，强制重新生成ZZ价格走势图
                            _generate_zz_price_chart_cached.clear()
                            logger.info("已清除XX价格走势图缓存，开始重新生成...")
                            
                            # 重新生成ZZ价格走势图
                            new_chart = _generate_zz_price_chart_cached(self.state.db_manager)
                            
                            if new_chart is not None:
                                # 更新缓存数据
                                self.state.show_tables_data['zz_price_chart'] = new_chart
                                
                                # 更新session_state中的数据
                                if hasattr(st.session_state, 'show_tables_data'):
                                    st.session_state.show_tables_data['zz_price_chart'] = new_chart
                                
                                # 设置更新时间
                                st.session_state.zz_chart_update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                                
                                st.success("✅ XX价格走势图更新成功！")
                                logger.info("XX价格走势图手动更新成功")
                                
                                # 刷新页面以显示最新数据
                                st.rerun()
                            else:
                                st.error("❌ XX价格走势图更新失败：生成图表为空")
                                logger.warning("XX价格走势图手动更新失败：生成图表为空")
                                
                        except Exception as e:
                            st.error(f"❌ 更新XX价格走势图时出错: {str(e)}")
                            logger.error(f"手动更新XX价格走势图失败: {str(e)}")
            
            # 检查是否有定时更新
            if self.state.check_and_clear_zz_chart_update_flag():
                st.success(f"🔄 XX价格走势图已自动更新 ({st.session_state.zz_chart_update_time})")
                # 强制刷新页面以显示最新数据
                st.rerun()
            
            # 从缓存中获取ZZ价格走势图
            zz_price_chart = self.state.show_tables_data.get('zz_price_chart')
            
            if zz_price_chart is not None:
                # 显示图片
                st.image(zz_price_chart, width=None)
                
                # 显示最后更新时间
                if hasattr(st.session_state, 'zz_chart_update_time'):
                    st.caption(f"最后更新: {st.session_state.zz_chart_update_time}")
                
                # 添加下载按钮
                st.download_button(
                    label="📥 下载XX价格走势图",
                    data=zz_price_chart,
                    file_name="xx_price_trend.png",
                    mime="image/png",
                    key="download_zz_price_chart"
                )
            else:
                st.info("""
                **暂无XX价格走势图数据**
                
                可能的原因：
                1. 数据库中没有最近14天的XX价格数据
                2. 数据表为空或查询条件不匹配
                3. 数据库连接问题
                
                建议：
                - 稍后再试
                - 联系管理员检查数据库状态
                - 确认数据表是否有数据更新
                """)
                
        except Exception as e:
            logger.error(f"显示ZZ价格走势图失败: {str(e)}")
            st.error(f"显示ZZ价格走势图失败: {str(e)}")
    
    def display_ahs_coupons(self):
        """显示爱回收优惠券数据"""
        try:
            st.write("**🍎爱回收苹果手机优惠券**")
            
            # 使用缓存的数据 - 恢复原来的显示逻辑
            summary_df = self.state.show_tables_data['ahs_coupons']
            
            if summary_df is not None and not summary_df.empty:
                # 检查是否是格式化后的数据
                if '价格下限' in summary_df.columns and '价格上限' in summary_df.columns and '优惠券额度' in summary_df.columns:
                    # 已经是格式化后的数据，直接显示
                    display_df = summary_df[['价格下限', '价格上限', '优惠券额度', 'package_codes']].copy()
                    display_df.columns = ['价格下限', '价格上限', '优惠券额度', 'Package Codes']
                else:
                    # 原始数据，需要格式化
                    formatted_data = _format_ahs_coupon_data(summary_df)
                    if not formatted_data.empty:
                        display_df = formatted_data[['价格下限', '价格上限', '优惠券额度', 'package_codes']].copy()
                        display_df.columns = ['价格下限', '价格上限', '优惠券额度', 'Package Codes']
                    else:
                        display_df = summary_df
                
                # 显示数据，去掉索引列
                st.dataframe(display_df, hide_index=True)
                
                # 下载按钮（Excel）
                output = io.BytesIO()
                with pd.ExcelWriter(output, engine='openpyxl') as writer:
                    display_df.to_excel(writer, sheet_name='数据', index=False)
                excel_data = output.getvalue()
                st.download_button(
                    label="📥 下载爱回收优惠券数据",
                    data=excel_data,
                    file_name="ahs_coupons.xlsx",
                    mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                    key="download_ahs_coupons"
                )
            else:
                st.warning("暂无爱回收优惠券数据")
                
        except Exception as e:
            logger.error(f"显示爱回收优惠券数据失败: {str(e)}")
            st.error(f"显示爱回收优惠券数据失败: {str(e)}")
    
    def display_zz_coupons(self):
        """显示转转优惠券数据"""
        try:
            st.write("**🍎XX手机优惠券**")
            
            # 使用缓存的数据
            coupon_zz_filtered = self.state.show_tables_data['zz_coupons']
            
            if coupon_zz_filtered is not None and not coupon_zz_filtered.empty:
                # 按照价格下限排序
                if '价格下限' in coupon_zz_filtered.columns:
                    coupon_zz_filtered = coupon_zz_filtered.sort_values('价格下限', ascending=True).reset_index(drop=True)
                
                # 显示数据，去掉索引列
                st.dataframe(coupon_zz_filtered, hide_index=True)
                
                # 下载按钮
                csv = coupon_zz_filtered.to_csv(index=False)
                # 改为Excel下载
                output = io.BytesIO()
                with pd.ExcelWriter(output, engine='openpyxl') as writer:
                    coupon_zz_filtered.to_excel(writer, sheet_name='数据', index=False)
                excel_data = output.getvalue()
                st.download_button(
                    label="📥 下载XX优惠券数据",
                    data=excel_data,
                    file_name="xx_coupons.xlsx",
                    mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                    key="download_zz_coupons"
                )
            else:
                st.warning("暂无转转优惠券数据")
                
        except Exception as e:
            logger.error(f"显示转转优惠券数据失败: {str(e)}")
            st.error(f"显示转转优惠券数据失败: {str(e)}")
    
    def display_price_competitiveness(self):
        """显示价格竞争力数据"""
        try:
            # 从缓存中获取价格竞争力数据
            price_competitiveness_data = self.state.show_tables_data.get('price_competitiveness')
            
            if price_competitiveness_data is not None and not price_competitiveness_data.empty:
                # 检查必要的列是否存在
                required_cols = ["product_name", "target", "inquiry_compete_rate", "cost_rate_estimated", "bi_ratio"]
                missing_cols = [col for col in required_cols if col not in price_competitiveness_data.columns]
                
                if missing_cols:
                    st.warning(f"价格竞争力数据缺少必要列: {missing_cols}")
                    st.dataframe(price_competitiveness_data, hide_index=True)
                else:
                    # 使用show_tables.py中的格式生成HTML表格
                    html_table = self.state.build_price_email_html(price_competitiveness_data)
                    
                    # 显示标题
                    st.markdown("""
                    <div align="center">
                        <p style="color:#1677ff; font-size:18px; font-weight:700; margin:8px 0;">
                            🍎苹果手机价格竞争力算法（0-95%）
                        </p>
                    </div>
                    """, unsafe_allow_html=True)
                    
                    # 显示HTML表格（build_price_email_html已经包含了完整的样式）
                    st.markdown(html_table, unsafe_allow_html=True)
                
                # 添加下载按钮（Excel）
                output = io.BytesIO()
                with pd.ExcelWriter(output, engine='openpyxl') as writer:
                    price_competitiveness_data.to_excel(writer, sheet_name='数据', index=False)
                excel_data = output.getvalue()
                st.download_button(
                    label="📥 下载价格竞争力数据",
                    data=excel_data,
                    file_name="price_competitiveness.xlsx",
                    mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                    key="download_price_competitiveness"
                )
            else:
                st.info("""
                **暂无价格竞争力数据**
                
                可能的原因：
                1. 今日数据尚未生成（通常需要等到下午）
                2. Excel文件路径不存在或无法访问
                3. 数据文件为空
                
                建议：
                - 稍后再试
                - 联系管理员检查数据生成状态
                - 检查服务器文件路径是否正确
                """)
                
        except Exception as e:
            logger.error(f"获取价格竞争力数据失败: {str(e)}")
            st.error(f"获取价格竞争力数据失败: {str(e)}")
    
    def display_strategy_recommendations(self):
        """显示智能策略推荐"""
        try:
            st.subheader("🤖 智能策略推荐")
            
            # 获取产品指标数据
            current_date = datetime.now().strftime('%Y-%m-%d')
            metrics_df = self.state.db_manager.get_product_metrics(current_date)
            
            if metrics_df.empty:
                st.info("""
                **暂无产品指标数据**
                
                可能的原因：
                1. 今日数据尚未生成
                2. 数据库中没有对应的产品数据
                3. 查询条件不匹配
                
                建议：
                - 稍后再试
                - 联系管理员检查数据状态
                """)
                return
            
            # 显示产品指标数据
            st.write("**📊 产品指标数据**")
            
            # 重命名列为中文
            metrics_df_display = metrics_df.copy()
            metrics_df_display.columns = [
                '日期', '型号名称', '库存量', '近3天日均出库量', 
                '预计周转天数', '近3天毛利率(%)', '近3天净利率(%)', '近3天追价费用率(%)'
            ]
            
            # 确保数值列保持正确的格式
            numeric_columns = ['库存量', '近3天日均出库量', '预计周转天数', '近3天毛利率(%)', '近3天净利率(%)', '近3天追价费用率(%)']
            for col in numeric_columns:
                if col in metrics_df_display.columns:
                    metrics_df_display[col] = pd.to_numeric(metrics_df_display[col], errors='coerce')
            
            # 检查是否有推荐结果需要横向拼接
            if hasattr(st.session_state, 'strategy_recommendations') and st.session_state.strategy_recommendations:
                # 创建推荐结果字典，以product_name为key
                recommendations_dict = {}
                for rec in st.session_state.strategy_recommendations:
                    recommendations_dict[rec.product_name] = {
                        '目标竞争力': f"{rec.selected_strategy.target:.1%}",
                        '推荐竞争力': f"{rec.selected_strategy.inquiry_compete_rate:.1%}",
                        'BI价距': f"{rec.selected_strategy.bi_ratio:.1%}",
                        '费用率': f"{rec.selected_strategy.cost_rate_estimated:.1%}",
                        '费用效率': f"{int(rec.efficiency_score)}",
                        'AI推荐原因': rec.reason
                    }
                
                # 为产品指标数据添加推荐结果列
                for col in ['目标竞争力', '推荐竞争力', 'BI价距', '费用率', '费用效率', 'AI推荐原因']:
                    metrics_df_display[col] = ''
                
                # 根据型号名称匹配推荐结果
                for idx, row in metrics_df_display.iterrows():
                    product_name = row['型号名称']
                    if product_name in recommendations_dict:
                        for col in ['目标竞争力', '推荐竞争力', 'BI价距', '费用率', '费用效率', 'AI推荐原因']:
                            metrics_df_display.at[idx, col] = recommendations_dict[product_name][col]
                
                # 使用样式显示表格，推荐结果列用浅绿色标识
                def highlight_recommendation_columns(row):
                    styles = [''] * len(row)
                    # 找到推荐结果列的索引
                    recommendation_cols = ['目标竞争力', '推荐竞争力', 'BI价距', '费用率', '费用效率', 'AI推荐原因']
                    for i, col in enumerate(row.index):
                        if col in recommendation_cols and row[col] != '':
                            styles[i] = 'background-color: #f0f8f0'
                    return styles
                
                # 统一设置显示格式，避免数值列出现过多小数或补零
                def fmt_int(v):
                    try:
                        return "" if pd.isna(v) else f"{int(round(float(v))):,}"
                    except Exception:
                        return v

                def fmt_float2(v):
                    try:
                        if pd.isna(v):
                            return ""
                        # 保留最多2位小数，去除末尾无意义的0和点
                        s = f"{float(v):.2f}"
                        s = s.rstrip('0').rstrip('.')
                        return s
                    except Exception:
                        return v

                formatters = {
                    '库存量': fmt_int,
                    '近3天日均出库量': fmt_int,
                    '预计周转天数': fmt_int,
                    '近3天毛利率(%)': fmt_float2,
                    '近3天净利率(%)': fmt_float2,
                    '近3天追价费用率(%)': fmt_float2,
                    # 推荐结果列也统一格式
                    '目标竞争力': fmt_float2,
                    '推荐竞争力': fmt_float2,
                    'BI价距': fmt_float2,
                    '费用率': fmt_float2,
                    '费用效率': fmt_int,
                }

                styled_df = (
                    metrics_df_display
                        .style
                        .apply(highlight_recommendation_columns, axis=1)
                        .format(formatters)
                )

                # 设置列宽，特别是AI推荐原因列（尽可能放宽）
                st.dataframe(
                    styled_df,
                    hide_index=True,
                    column_config={
                        "AI推荐原因": st.column_config.TextColumn(
                            "AI推荐原因",
                            help="AI推荐的详细原因说明",
                            width=800
                        )
                    }
                )
                
                # 提供Excel下载
                output = io.BytesIO()
                with pd.ExcelWriter(output, engine='openpyxl') as writer:
                    metrics_df_display.to_excel(writer, sheet_name='产品指标与推荐结果', index=False)
                excel_data = output.getvalue()
                st.download_button(
                    label="📥 下载产品指标与推荐结果",
                    data=excel_data,
                    file_name=f"product_metrics_and_recommendations_{current_date}.xlsx",
                    mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                    key="download_combined_data"
                )
            else:
                # 没有推荐结果时，只显示产品指标数据
                st.dataframe(metrics_df_display, hide_index=True)
                
                # 提供Excel下载
                output = io.BytesIO()
                with pd.ExcelWriter(output, engine='openpyxl') as writer:
                    metrics_df_display.to_excel(writer, sheet_name='产品指标', index=False)
                excel_data = output.getvalue()
                st.download_button(
                    label="📥 下载产品指标数据",
                    data=excel_data,
                    file_name=f"product_metrics_{current_date}.xlsx",
                    mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                    key="download_product_metrics"
                )
            
            # 获取价格竞争力数据用于策略选择
            price_competitiveness_data = self.state.show_tables_data.get('price_competitiveness')
            
            if price_competitiveness_data is None or price_competitiveness_data.empty:
                st.warning("需要价格竞争力数据才能进行策略推荐，请稍后再试")
                return
            
            # 转换产品指标数据
            products_metrics = []
            for _, row in metrics_df.iterrows():
                metrics = ProductMetrics(
                    product_name=row['product_name'],
                    partition_date=str(row['partition_date']),
                    stock_nums=int(row['stock_nums']),
                    avg_sales_cnt_3days=float(row['avg_sales_cnt_3days']),
                    expected_inventory_turnover=float(row['expected_inventory_turnover']),
                    gross_margin_3_day=float(row['gross_margin_3_day']),
                    net_profit_margin_3_day=float(row['net_profit_margin_3_day']),
                    chaseup_cost_rate_3_day=float(row['chaseup_cost_rate_3_day'])
                )
                products_metrics.append(metrics)
            
            # 为每个产品构建可用策略
            products_strategies = {}
            for metrics in products_metrics:
                # 从价格竞争力数据中筛选该产品的策略
                product_data = price_competitiveness_data[
                    price_competitiveness_data['product_name'] == metrics.product_name
                ]
                
                if not product_data.empty:
                    strategies = []
                    for _, strategy_row in product_data.iterrows():
                        strategy = PriceStrategy(
                            target=float(strategy_row['target']),
                            bi_ratio=float(strategy_row['bi_ratio']),
                            inquiry_compete_rate=float(strategy_row['inquiry_compete_rate']),
                            cost_rate_estimated=float(strategy_row['cost_rate_estimated']),
                            efficiency=0.0  # 将在选择器中计算
                        )
                        strategies.append(strategy)
                    
                    products_strategies[metrics.product_name] = strategies
            
            if not products_strategies:
                st.warning("没有找到可用的价格策略数据")
                return
            
            # 显示策略推荐按钮
            if st.button("🚀 开始智能策略推荐", type="primary"):
                with st.spinner("正在调用大模型进行策略推荐..."):
                    try:
                        # 调用策略选择器
                        recommendations = self.strategy_selector.select_strategies_for_products(
                            products_metrics, products_strategies
                        )
                        
                        if recommendations:
                            # 将推荐结果存储到session_state中
                            st.session_state.strategy_recommendations = recommendations
                            st.success("策略推荐生成成功！")
                            st.rerun()
                        else:
                            st.warning("未能生成策略推荐，请检查数据或稍后再试")
                            
                    except Exception as e:
                        logger.error(f"策略推荐失败: {str(e)}")
                        st.error(f"策略推荐失败: {str(e)}")
                        
        except Exception as e:
            logger.error(f"显示策略推荐失败: {str(e)}")
            st.error(f"显示策略推荐失败: {str(e)}")
    
    def _display_recommendation_results(self, recommendations: List[StrategyRecommendation]):
        """显示推荐结果"""
        st.subheader("🎯 推荐结果")
        
        # 创建结果表格（处理效率为无穷/非数的情况）
        import math
        results_data = []
        for rec in recommendations:
            eff = rec.efficiency_score
            if eff is None or isinstance(eff, str):
                eff_display = ""
            else:
                try:
                    eff_display = "∞" if not math.isfinite(float(eff)) else str(int(round(float(eff))))
                except Exception:
                    eff_display = ""
            results_data.append({
                '型号': rec.product_name,
                '推荐竞争力': f"{rec.selected_strategy.inquiry_compete_rate:.1%}",
                'BI价距': f"{rec.selected_strategy.bi_ratio:.1%}",
                '费用率': f"{rec.selected_strategy.cost_rate_estimated:.1%}",
                '费用效率': eff_display,
                'AI推荐原因': rec.reason  # 显示完整原因，不截断
            })
        
        results_df = pd.DataFrame(results_data)
        st.dataframe(results_df, hide_index=True)
        
        # 提供Excel下载
        output = io.BytesIO()
        with pd.ExcelWriter(output, engine='openpyxl') as writer:
            results_df.to_excel(writer, sheet_name='策略推荐', index=False)
        excel_data = output.getvalue()
        st.download_button(
            label="📥 下载策略推荐结果",
            data=excel_data,
            file_name=f"strategy_recommendations_{datetime.now().strftime('%Y%m%d')}.xlsx",
            mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            key="download_strategy_recommendations"
        )
        
        # 显示详细分析
        with st.expander("📈 详细分析", expanded=False):
            for rec in recommendations:
                st.write(f"**{rec.product_name}**")
                
                col1, col2 = st.columns(2)
                with col1:
                    st.metric("推荐竞争力", f"{rec.selected_strategy.inquiry_compete_rate:.1%}")
                    st.metric("BI价距", f"{rec.selected_strategy.bi_ratio:.1%}")
                
                with col2:
                    st.metric("费用率", f"{rec.selected_strategy.cost_rate_estimated:.1%}")
                    st.metric("费用效率", f"{rec.efficiency_score:.2f}")
                
                st.write(f"**选择原因**: {rec.reason}")
                
                # 显示约束条件
                if rec.constraints_applied:
                    st.write("**应用约束**:")
                    for key, value in rec.constraints_applied.items():
                        st.write(f"- {key}: {value}")
                
                st.markdown("---")

    
    def run(self):
        """运行应用"""
        try:
            # 渲染页面头部
            self.render_header()
            
            # 渲染侧边栏并获取设置
            settings = self.render_sidebar()
            
            # 显示show_tables数据（移到最上面）
            self.display_show_tables_data()
            
            # 渲染产品选择
            products = self.render_product_selection(
                settings['category'], 
                settings['brand']
            )
            
            # 渲染竞争力设置
            competitiveness_dict = self.render_competitiveness_settings(products)
            
            # 渲染处理控制
            self.render_processing_controls(products, competitiveness_dict, settings)
            
            # 检查并显示之前的结果（如果存在）
            if hasattr(st.session_state, 'processing_results') and st.session_state.processing_results:
                self.display_results_summary(st.session_state.processing_results)
                
        except Exception as e:
            logger.error(f"应用运行出错: {str(e)}")
            st.error(f"应用运行出错: {str(e)}")

def main():
    """主函数"""
    # try:
    #     app = NewPriceStrategyApp()
    #     app.run()
    # except Exception as e:
    #     st.error(f"应用启动失败: {str(e)}")
    #     logger.error(f"应用启动失败: {str(e)}")

    app = NewPriceStrategyApp()
    app.run()

if __name__ == "__main__":
    main()
    
    # 激活环境 source activate ml
    # 启动： nohup streamlit run new_app.py --server.port 8502 --server.address 0.0.0.0 &
    # 启动： streamlit run coupon_manager.py --server.port 8503 --server.address 0.0.0.0